This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.
Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter. # Tue Jun 7 19:35:18 2022 ——————————
source("tianfengRwrappers.R")
Registered S3 method overwritten by 'data.table':
method from
print.data.table
载入需要的程辑包:dplyr
载入程辑包:‘dplyr’
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
载入需要的程辑包:reticulate
载入需要的程辑包:tidyr
载入程辑包:‘MySeuratWrappers’
The following objects are masked from ‘package:Seurat’:
DimPlot, DoHeatmap, LabelClusters, RidgePlot, VlnPlot
载入程辑包:‘cowplot’
The following object is masked from ‘package:ggpubr’:
get_legend
载入需要的程辑包:viridisLite
载入程辑包:‘reshape2’
The following object is masked from ‘package:tidyr’:
smiths
NOTE: Either Arial Narrow or Roboto Condensed fonts are required to use these themes.
Please use hrbrthemes::import_roboto_condensed() to install Roboto Condensed and
if Arial Narrow is not on your system, please see https://bit.ly/arialnarrow
Registered S3 method overwritten by 'enrichplot':
method from
fortify.enrichResult DOSE
clusterProfiler v3.14.3 For help: https://guangchuangyu.github.io/software/clusterProfiler
If you use clusterProfiler in published research, please cite:
Guangchuang Yu, Li-Gen Wang, Yanyan Han, Qing-Yu He. clusterProfiler: an R package for comparing biological themes among gene clusters. OMICS: A Journal of Integrative Biology. 2012, 16(5):284-287.
Registering fonts with R
载入程辑包:‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
载入需要的程辑包:Biobase
载入需要的程辑包:BiocGenerics
载入需要的程辑包:parallel
载入程辑包:‘BiocGenerics’
The following objects are masked from ‘package:parallel’:
clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport, clusterMap, parApply,
parCapply, parLapply, parLapplyLB, parRapply, parSapply, parSapplyLB
The following objects are masked from ‘package:dplyr’:
combine, intersect, setdiff, union
The following objects are masked from ‘package:stats’:
IQR, mad, sd, var, xtabs
The following objects are masked from ‘package:base’:
anyDuplicated, append, as.data.frame, basename, cbind, colnames, dirname, do.call, duplicated,
eval, evalq, Filter, Find, get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply, match,
mget, order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank, rbind, Reduce, rownames,
sapply, setdiff, sort, table, tapply, union, unique, unsplit, which, which.max, which.min
Welcome to Bioconductor
Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor, see
'citation("Biobase")', and for packages 'citation("pkgname")'.
载入需要的程辑包:e1071
载入程辑包:‘widgetTools’
The following object is masked from ‘package:dplyr’:
funs
载入程辑包:‘DynDoc’
The following object is masked from ‘package:BiocGenerics’:
path
载入程辑包:‘DT’
The following object is masked from ‘package:Seurat’:
JS
========================================
circlize version 0.4.13
CRAN page: https://cran.r-project.org/package=circlize
Github page: https://github.com/jokergoo/circlize
Documentation: https://jokergoo.github.io/circlize_book/book/
If you use it in published research, please cite:
Gu, Z. circlize implements and enhances circular visualization
in R. Bioinformatics 2014.
This message can be suppressed by:
suppressPackageStartupMessages(library(circlize))
========================================
载入需要的程辑包:grid
========================================
ComplexHeatmap version 2.2.0
Bioconductor page: http://bioconductor.org/packages/ComplexHeatmap/
Github page: https://github.com/jokergoo/ComplexHeatmap
Documentation: http://jokergoo.github.io/ComplexHeatmap-reference
If you use it in published research, please cite:
Gu, Z. Complex heatmaps reveal patterns and correlations in multidimensional
genomic data. Bioinformatics 2016.
========================================
载入程辑包:‘ComplexHeatmap’
The following object is masked from ‘package:plotly’:
add_heatmap
载入需要的程辑包:AnnotationDbi
载入需要的程辑包:stats4
载入需要的程辑包:IRanges
载入需要的程辑包:S4Vectors
载入程辑包:‘S4Vectors’
The following object is masked from ‘package:plotly’:
rename
The following object is masked from ‘package:tidyr’:
expand
The following objects are masked from ‘package:dplyr’:
first, rename
The following object is masked from ‘package:base’:
expand.grid
载入程辑包:‘IRanges’
The following object is masked from ‘package:plotly’:
slice
The following objects are masked from ‘package:dplyr’:
collapse, desc, slice
载入程辑包:‘AnnotationDbi’
The following object is masked from ‘package:plotly’:
select
The following object is masked from ‘package:dplyr’:
select
rat Frontier GSE174098 carotid
rat10x <- CreateSeuratObject(Read10X("./rat_scRNAseq/"), names.field = 2, names.delim = "-",
project = "rat", min.cells = 10, min.features = 300) %>%
PercentageFeatureSet(pattern = "^Mt-", col.name = "percent.mt")
table(rat10x$orig.ident)
VlnPlot(rat10x,"nCount_RNA") /
VlnPlot(rat10x,"percent.mt") /
VlnPlot(rat10x, "nFeature_RNA")
rat10x <- rat10x %>% subset(subset = nFeature_RNA > 400 & nFeature_RNA < 4000 &
nCount_RNA > 1000 & nCount_RNA < 30000 & percent.mt< 10) %>%
SCTransform(vars.to.regress = "percent.mt", verbose = F) %>%
RunPCA() %>% FindNeighbors(dims = 1:20) %>%
RunUMAP(dims = 1:20) %>%
FindClusters(resolution = 0.1)

mouse10x <- CreateSeuratObject(Read10X("./celldiscovery_mouse_10x/1/"), names.field = 2,
names.delim = "-",project = "mouse1", min.cells = 10,
min.features = 300) %>%
PercentageFeatureSet(pattern = "^mt-", col.name = "percent.mt")
mouse10x2 <- CreateSeuratObject(Read10X("./celldiscovery_mouse_10x/2/"), names.field = 2, names.delim = "-",
project = "mouse2", min.cells = 10, min.features = 300) %>%
PercentageFeatureSet(pattern = "^mt-", col.name = "percent.mt")
mouse10x <- merge(mouse10x,mouse10x2)
VlnPlot(mouse10x,"nCount_RNA") /
VlnPlot(mouse10x,"percent.mt") /
VlnPlot(mouse10x, "nFeature_RNA")
mouse10x <- mouse10x %>% subset(subset = nFeature_RNA > 500 & nFeature_RNA < 5000 &
nCount_RNA > 1000 & nCount_RNA < 30000 & percent.mt < 10) %>%
SCTransform(vars.to.regress = "percent.mt", verbose = F) %>%
RunPCA() %>% FindNeighbors(dims = 1:20) %>%
RunUMAP(dims = 1:20) %>%
FindClusters(resolution = 0.1)
# table(mouse10x$orig.ident)
umapplot(mouse10x)
f("Lmo2",mouse10x)

mouse coronary GSE131778
mouse_coronary_countmatrix <- read.csv("./GSE131776_mouse_scRNAseq.txt", sep = "\t")
func <- function(s) {
paste0(strsplit(s, ".", fixed = T)[[1]][2], "_", strsplit(s, ".", fixed = T)[[1]][1])
}
colnames(mouse_coronary_countmatrix) <- lapply(colnames(mouse_coronary_countmatrix), func) # 拆分样本
mousecor <- CreateSeuratObject(counts = mouse_coronary_countmatrix,
project = "mouse_cor", min.cells = 10, min.features = 300) %>% PercentageFeatureSet(pattern = "^mt-", col.name = "percent.mt")
# saveRDS(mousecor,"mousecor.rds")
table(mousecor$orig.ident)
VlnPlot(mousecor,"nCount_RNA") /
VlnPlot(mousecor,"percent.mt") /
VlnPlot(mousecor, "nFeature_RNA")
mousecor <- mousecor %>% subset(subset = nFeature_RNA > 400 & nFeature_RNA < 4000 &
nCount_RNA > 1000 & nCount_RNA < 30000 & percent.mt < 10) %>%
SCTransform(vars.to.regress = "percent.mt", verbose = F) %>%
RunPCA() %>% FindNeighbors(dims = 1:20) %>%
RunUMAP(dims = 1:20) %>%
FindClusters(resolution = 0.1)
table(mousecor$orig.ident)
saveRDS(mousecor,"mousecor.rds")
SMC2
mousecor <- readRDS("mousecor.rds")
f("Prdm16",mousecor, label.size = 7) + theme(legend.text = element_text(size = 20))
umapplot(mousecor_stromal)
Scale for 'colour' is already present. Adding another scale for 'colour', which will
replace the existing scale.

mouse carotid scRNAseq GSE155513
dataload
count_mats <- list.files("./GSE155513_RAW/")
count_mats <- count_mats[count_mats != "sampleinfo.txt"]
allList <- lapply(count_mats, function(file) {
dd <- read.table(paste0("./GSE155513_RAW/", file), row.names = 1,stringsAsFactors = F)
colnames(dd) <- as.character(dd['gene',])
dd <- dd[-1,]
CreateSeuratObject(
counts = dd,
project = file, min.cells = 10, min.features = 300
)
})
# 合并seurat对象
mouse_carotid <- merge(allList[[1]],
y = allList[-1], add.cell.ids = count_mats,
project = "mouse_carotid"
)
rm(allList)
# saveRDS(mouse_carotid,"mouse_carotid.rds")
process
mouse_carotid <- readRDS("mouse_carotid.rds")
mouse_carotid <- mouse_carotid %>% PercentageFeatureSet(pattern = "^mt-", col.name = "percent.mt") %>%
subset(subset = nFeature_RNA > 500 & nFeature_RNA < 3000 &
nCount_RNA > 1000 & nCount_RNA < 20000 & percent.mt < 10) %>%
SCTransform(vars.to.regress = "percent.mt", verbose = F) %>%
RunPCA() %>% FindNeighbors(dims = 1:20) %>%
RunUMAP(dims = 1:20) %>%
FindClusters(resolution = 0.1)
saveRDS(mouse_carotid,"mouse_carotid.rds")
SMC
mouse_carotid <- readRDS("mouse_carotid.rds")
umapplot(mouse_carotid)
umapplot(mouse_carotid,group.by = "orig.ident",label = F)
f("Prdm16",mouse_carotid) #SMC
f("Ly6a",mouse_carotid) #SEM-like cells
multi_featureplot(c("Bmp2","Bmp4","Bmp6"),mouse_carotid)
## BMP4 在这里EC的*大部分*中表达,而在人类样本中BMP4+ EC细胞是少数的
markers <- FindAllMarkers(mouse_carotid,logfc.threshold = 0.5,min.diff.pct = 0.2, only.pos = T)
stromal cells
mouse_carotid_stromal <- subset(mouse_carotid,idents = c(0,2,1,7))
mouse_carotid_stromal <- mouse_carotid_stromal %>% RunPCA() %>% FindNeighbors(dims = 1:20) %>%
RunUMAP(dims = 1:20) %>% FindClusters(resolution = 0.1)
mouse_carotid_stromal <- mouse_carotid_stromal %>% FindClusters(resolution = 0.2)
mouse_carotid_stromal <- readRDS("mouse_carotid_stromal.rds")
# saveRDS(mouse_carotid_stromal,"mouse_carotid_stromal.rds")
umapplot(mouse_carotid_stromal)
f("Dlx2",mouse_carotid_stromal) #Dlx2,Dlx5,Dlx6共同定位
mouse_carotid_stromal <- AddModuleScore(mouse_carotid_stromal, list(mmSMC2_marker), name = "SMC2_score")
f("SMC2_score1", mouse_carotid_stromal,min.cutoff = 0)
multi_featureplot(mmSMC2_marker[1:9],mouse_carotid_stromal,labels = NA, label = F)
分群表
table(group_tab[Dlx5poscells])/table(group_tab)
GSM4705592_RPS003_matrix.txt.gz GSM4705593_RPS004_matrix.txt.gz GSM4705594_RPS011_matrix.txt.gz GSM4705595_RPS012_matrix.txt.gz GSM4705596_RPS007_matrix.txt.gz
0.080645161 0.076949502 0.130000000 0.029882604 0.058282209
GSM4705597_RPS008_matrix.txt.gz GSM4705598_RPS001_matrix.txt.gz GSM4705599_RPS002_matrix.txt.gz GSM4705600_RPS017_matrix.txt.gz GSM4705601_RPS018_matrix.txt.gz
0.004746835 0.039230575 0.003976143 0.093492209 0.095634096
GSM4705602_RPS013_matrix.txt.gz GSM4705603_RPS014_matrix.txt.gz GSM4705604_RPS015_matrix.txt.gz GSM4705605_RPS016_matrix.txt.gz
0.066773504 0.019255456 0.042503503 0.030303030
图
umapplot(mouse_carotid_stromal,label.size = 6,label = F) %>% ggsave("./fig7_mouse/cir_mouse_carotid_stromalumap2.png",plot = ., height = 5, width = 6,device = png)
Scale for 'colour' is already present. Adding another scale for 'colour', which will
replace the existing scale.
XGBoost 跨物种
umapplot(mouse_carotid_stromal, group.by = "scmap_idents")
Scale for 'colour' is already present. Adding another scale for 'colour', which will
replace the existing scale.

# run xgboost wrapper
mousecor_stromal <- XGBoost_predict_from_seuobj(mousecor_stromal, bst_model,
celltype_assign = 2)
Warning in XGBoost_predict_from_seuobj(mousecor_stromal, bst_model, celltype_assign = 2) :
强制改变过程中产生了NA
Warning in XGBoost_predict_from_seuobj(mousecor_stromal, bst_model, celltype_assign = 2) :
Please ensure that seurat idents are in numeric forms
[1] "ARI = 0.62988464111349"
[1] "return a seurat object with meta.data'X1'~'Xn'"
[1] "return a seurat object with meta.data'projected_idents'"
# run xgboost wrapper
mousecor_stromal <- XGBoost_predict_from_seuobj(mousecor_stromal, bst_model,
celltype_assign = 2)
Warning in XGBoost_predict_from_seuobj(mousecor_stromal, bst_model, celltype_assign = 2) :
强制改变过程中产生了NA
Warning in XGBoost_predict_from_seuobj(mousecor_stromal, bst_model, celltype_assign = 2) :
Please ensure that seurat idents are in numeric forms
[1] "ARI = 0.62988464111349"
[1] "return a seurat object with meta.data'X1'~'Xn'"
[1] "return a seurat object with meta.data'projected_idents'"
mousecor_stromal <- project2ref_celltype(mousecor_stromal, ds2)
umapplot(mousecor_stromal, group.by = "ref_celltype")
Scale for 'colour' is already present. Adding another scale for 'colour', which will
replace the existing scale.

human ds2
p <- multi_featureplot(c("ACTA2","CNN1","FN1","LUM","VCAM1","LY6A","DLX5","DLX6","LGALS3","SOST"), ds2, labels = NA, label.size = 6)
ggsave("refds2_carotid_stromal.png",plot = p, height = 12, width = 12,device = png)
p <- multi_featureplot(c("FRZB","SOST","DLX5","DLX6"), ds2, labels = NA, label.size = 6)
ggsave("refds2_SMC2_carotid_stromal.png",plot = p, height = 7, width = 7,device = png)
human bulk RNA-seq GSE120521 carotid stable/unstable FPKM
fpkm2tpm <- function(fpkm){
exp(log(fpkm) - log(sum(fpkm)) + log(1e6))
}
fpkm_matrix <- read.csv("GSE120521_FPKM.csv")
# fpkm_matrix <- distinct(fpkm_matrix) #去除重复行
fpkm_matrix <- fpkm_matrix[!duplicated(fpkm_matrix$name),]
rownames(fpkm_matrix) <- fpkm_matrix$name
fpkm_matrix$name <- NULL
tpm_matrix <- apply(fpkm_matrix, 2, fpkm2tpm)
colSums(tpm_matrix)
group_file <- c("stable","unstable","stable","unstable",
"stable","unstable","stable","unstable")
boxplot(tpm_matrix, las = 2)
expr_mat <- tpm_matrix[!apply(tpm_matrix, 1, function(x){sum(floor(x) == 0)>3}),]
boxplot(expr_mat, las = 2)
library(limma)
expr_mat <- normalizeBetweenArrays(expr_mat)
expr_mat <- log2(expr_mat+1) #使用log2 scale
#PCA
library(ggfortify)
df <- as.data.frame(t(expr_mat))
df$group <- group_file
autoplot(prcomp(df[,1:(ncol(df)-1)]), data=df, colour = 'group')+ theme_bw()
# If the sequencing depth is reasonably consistent across the RNA samples, then the simplest and most robust approach to differential exis to use limma-trend.
fit <- lmFit(expr_mat, group_file)
fit <- treat(fit, lfc=log2(1.2), trend=TRUE)
topTreat(fit, coef=ncol(design))
library(ggpubr)
dat <- expr_mat
design <- model.matrix(~factor(group_file))
fit <- lmFit(dat, design)
fit <- eBayes(fit)
# options(digits = 4)
topTable(fit,coef=2,adjust='BH')
deg <- topTable(fit,coef=2,adjust='BH',number = Inf)
head(deg)
write.csv(deg,"./datatable/stable vs unstable.csv")
## look up FRZB, SOST, PRDM6, OGN
ds1markers[ds1markers$cluster == "SMC2",]$gene
ds2markers[ds2markers$cluster == "SMC3",]$gene
smc2markers <- intersect(ds1markers[ds1markers$cluster == "SMC2",]$gene, ds2markers[ds2markers$cluster == "SMC3",]$gene)
deg[intersect(smc2markers, rownames(deg)),]
ds1markers[ds1markers$cluster == "SMC1",]$gene
ds2markers[ds2markers$cluster == "SMC1",]$gene
SMC1markers <- intersect(ds1markers[ds1markers$cluster == "SMC1",]$gene, ds2markers[ds2markers$cluster == "SMC1",]$gene)
deg[intersect(SMC1markers, rownames(deg)),]
# logFC_threshold = 1
# pvalue_threshold = 0.05
selected_genes = as.character(read.table("SMC2")$V1)
volcano_plot <- function(filename, selected_genes, logFC_threshold = 1, pvalue_threshold = 0.05)
{
f<-read.csv("./datatable/stable vs unstable.csv")
f$threshold <- factor(ifelse(f$adj.P.Val < pvalue_threshold & abs(f$logFC) >= logFC_threshold,
ifelse(f$logFC>= logFC_threshold ,'Up','Down'),'N.S.'),
levels=c('Up','Down','N.S.'))
ggplot(f,aes(x=logFC,y=-log10(adj.P.Val),color=threshold))+
geom_point()+
scale_color_manual(values=c("#CC0000","#2f5688","#BBBBBB"))+
geom_text_repel(
data = f[f$X %in% selected_genes,],
aes(label = X),
size = 5, max.overlaps = 1000,
col="black", segment.color = "black", show.legend = FALSE )+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
panel.background = element_blank(), axis.line = element_line(colour = "black"))+
theme(
axis.title.x = element_text(size = 20), axis.text.x = element_text(size = 15),
axis.title.y = element_text(size = 20), axis.text.y = element_text(size = 15),
legend.text = element_text(size = 20),
legend.title = element_blank()
)+
ylab('-log10 (p-adj)') +
xlab('log2 (FoldChange)') +
geom_vline(xintercept=c(-logFC_threshold,logFC_threshold), lty=3,col="black",lwd=0.5) +
geom_hline(yintercept =c(0,-log10(pvalue_threshold)),lty=3,col="black",lwd=0.5)
}
selected_genes <- c(as.character(read.table("SMC2")$V1))
p <- volcano_plot("./datatable/stable vs unstable.csv",selected_genes) + ggtitle("stable vs unstable, SMC2 marker")
ggsave("SMC2_stable vs unstable.png",plot = p,device = png,height = 4, width = 6)
Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAojIFR1ZSBKdW4gIDcgMTk6MzU6MTggMjAyMiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgpgYGB7cn0Kc291cmNlKCJ0aWFuZmVuZ1J3cmFwcGVycy5SIikKYGBgCgojIHJhdCBGcm9udGllciAqR1NFMTc0MDk4KiBjYXJvdGlkCmBgYHtyIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTh9CnJhdDEweCA8LSBDcmVhdGVTZXVyYXRPYmplY3QoUmVhZDEwWCgiLi9yYXRfc2NSTkFzZXEvIiksIG5hbWVzLmZpZWxkID0gMiwgbmFtZXMuZGVsaW0gPSAiLSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvamVjdCA9ICJyYXQiLCBtaW4uY2VsbHMgPSAxMCwgbWluLmZlYXR1cmVzID0gMzAwKSAlPiUKICBQZXJjZW50YWdlRmVhdHVyZVNldChwYXR0ZXJuID0gIl5NdC0iLCBjb2wubmFtZSA9ICJwZXJjZW50Lm10IikgCgp0YWJsZShyYXQxMHgkb3JpZy5pZGVudCkKVmxuUGxvdChyYXQxMHgsIm5Db3VudF9STkEiKSAvClZsblBsb3QocmF0MTB4LCJwZXJjZW50Lm10IikgLwpWbG5QbG90KHJhdDEweCwgIm5GZWF0dXJlX1JOQSIpCgoKcmF0MTB4IDwtIHJhdDEweCAlPiUgc3Vic2V0KHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDQwMCAmIG5GZWF0dXJlX1JOQSA8IDQwMDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuQ291bnRfUk5BID4gMTAwMCAmICBuQ291bnRfUk5BIDwgMzAwMDAgJiBwZXJjZW50Lm10PCAxMCkgJT4lCiAgICBTQ1RyYW5zZm9ybSh2YXJzLnRvLnJlZ3Jlc3MgPSAicGVyY2VudC5tdCIsIHZlcmJvc2UgPSBGKSAlPiUgCiAgICBSdW5QQ0EoKSAlPiUgRmluZE5laWdoYm9ycyhkaW1zID0gMToyMCkgJT4lIAogICAgUnVuVU1BUChkaW1zID0gMToyMCkgJT4lIAogICAgRmluZENsdXN0ZXJzKHJlc29sdXRpb24gPSAwLjEpCgpgYGAKCgpgYGB7ciBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0zfQp1bWFwcGxvdChyYXQxMHgpCm11bHRpX2ZlYXR1cmVwbG90KGMoIkFjdGEyIiwiQ25uMSIsIkZuMSIsIkx1bSIsIlZjYW0xIiwiTHk2YSIsIkRseDUiLCJEbHg2IiwiU29zdCIpLCByYXQxMHgpCgoKZigiQm1wcjFiIiwgcmF0MTB4KSAvCmYoIkJtcHIxYSIsIHJhdDEweCkKdGFibGUocmF0MTB4JHNldXJhdF9jbHVzdGVycykKCnJhdDEweDAgPC0gc3Vic2V0KHJhdDEweCxpZGVudCA9IDApCgpmKCJTb3N0IixyYXQxMHgpCm5jb2woc3Vic2V0KHJhdDEweDAsIHN1YnNldCA9IEJtcHIxYSA+IDApKQpuY29sKHN1YnNldChyYXQxMHgwLCBzdWJzZXQgPSBCbXByMWIgPiAwKSkKbmNvbChzdWJzZXQocmF0MTB4MCwgc3Vic2V0ID0gQm1wcjFhID4gMCAmIEJtcHIxYiA+IDApKQoKbmNvbChzdWJzZXQocmF0MTB4MCwgc3Vic2V0ID0gU29zdCA+IDAgJiBCbXByMWIgPiAwKSkKbmNvbChzdWJzZXQocmF0MTB4MCwgc3Vic2V0ID0gU29zdCA+IDAgJiBCbXByMWEgPiAwKSkKCm5jb2wocmF0MTB4MCkKYGBgCgoKYGBge3J9Cm1vdXNlMTB4IDwtIENyZWF0ZVNldXJhdE9iamVjdChSZWFkMTBYKCIuL2NlbGxkaXNjb3ZlcnlfbW91c2VfMTB4LzEvIiksIG5hbWVzLmZpZWxkID0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzLmRlbGltID0gIi0iLHByb2plY3QgPSAibW91c2UxIiwgbWluLmNlbGxzID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZmVhdHVyZXMgPSAzMDApICU+JQogIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KHBhdHRlcm4gPSAiXm10LSIsIGNvbC5uYW1lID0gInBlcmNlbnQubXQiKSAKCm1vdXNlMTB4MiA8LSBDcmVhdGVTZXVyYXRPYmplY3QoUmVhZDEwWCgiLi9jZWxsZGlzY292ZXJ5X21vdXNlXzEweC8yLyIpLCBuYW1lcy5maWVsZCA9IDIsIG5hbWVzLmRlbGltID0gIi0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3QgPSAibW91c2UyIiwgbWluLmNlbGxzID0gMTAsIG1pbi5mZWF0dXJlcyA9IDMwMCkgJT4lCiAgUGVyY2VudGFnZUZlYXR1cmVTZXQocGF0dGVybiA9ICJebXQtIiwgY29sLm5hbWUgPSAicGVyY2VudC5tdCIpIAoKbW91c2UxMHggPC0gbWVyZ2UobW91c2UxMHgsbW91c2UxMHgyKQoKVmxuUGxvdChtb3VzZTEweCwibkNvdW50X1JOQSIpIC8KVmxuUGxvdChtb3VzZTEweCwicGVyY2VudC5tdCIpIC8KVmxuUGxvdChtb3VzZTEweCwgIm5GZWF0dXJlX1JOQSIpCgptb3VzZTEweCA8LSBtb3VzZTEweCAlPiUgc3Vic2V0KHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDUwMCAmIG5GZWF0dXJlX1JOQSA8IDUwMDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuQ291bnRfUk5BID4gMTAwMCAmICBuQ291bnRfUk5BIDwgMzAwMDAgJiBwZXJjZW50Lm10IDwgMTApICU+JQogICAgU0NUcmFuc2Zvcm0odmFycy50by5yZWdyZXNzID0gInBlcmNlbnQubXQiLCB2ZXJib3NlID0gRikgJT4lIAogICAgUnVuUENBKCkgJT4lIEZpbmROZWlnaGJvcnMoZGltcyA9IDE6MjApICU+JSAKICAgIFJ1blVNQVAoZGltcyA9IDE6MjApICU+JSAKICAgIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gMC4xKQojIHRhYmxlKG1vdXNlMTB4JG9yaWcuaWRlbnQpCgoKdW1hcHBsb3QobW91c2UxMHgpCmYoIkxtbzIiLG1vdXNlMTB4KQpgYGAKCgpgYGB7cn0KU01DMl9tYXJrZXIgPC0gYXMuY2hhcmFjdGVyKHJlYWQudGFibGUoIlNNQzIiKSRWMSkKbGlicmFyeShob21vbG9nZW5lKQoKbW1TTUMyX21hcmtlciA8LSBob21vbG9nZW5lKFNNQzJfbWFya2VyLCBpblRheCA9IDk2MDYsIG91dFRheCA9IDEwMDkwKQptbVNNQzJfbWFya2VyIDwtIG1tU01DMl9tYXJrZXIkYDEwMDkwYAptbVNNQzJfbWFya2VyIDwtIGludGVyc2VjdChyb3duYW1lcyhtb3VzZTEweCksIG1tU01DMl9tYXJrZXIpCm1vdXNlMTB4IDwtIEFkZE1vZHVsZVNjb3JlKG1vdXNlMTB4LCBsaXN0KG1tU01DMl9tYXJrZXIpLCBuYW1lID0gIlNNQzJfc2NvcmUiKQpmKCJTTUMyX3Njb3JlMSIsbW91c2UxMHgsIG1pbi5jdXRvZmYgPSAwKQpEb3RwbG90KCJTTUMyX3Njb3JlMSIsbW91c2UxMHgpCmBgYAoKCiMgbW91c2UgY29yb25hcnkgKkdTRTEzMTc3OCoKYGBge3J9Cm1vdXNlX2Nvcm9uYXJ5X2NvdW50bWF0cml4IDwtIHJlYWQuY3N2KCIuL0dTRTEzMTc3Nl9tb3VzZV9zY1JOQXNlcS50eHQiLCBzZXAgPSAiXHQiKQpmdW5jIDwtIGZ1bmN0aW9uKHMpIHsKICBwYXN0ZTAoc3Ryc3BsaXQocywgIi4iLCBmaXhlZCA9IFQpW1sxXV1bMl0sICJfIiwgc3Ryc3BsaXQocywgIi4iLCBmaXhlZCA9IFQpW1sxXV1bMV0pCn0KY29sbmFtZXMobW91c2VfY29yb25hcnlfY291bnRtYXRyaXgpIDwtIGxhcHBseShjb2xuYW1lcyhtb3VzZV9jb3JvbmFyeV9jb3VudG1hdHJpeCksIGZ1bmMpICMg5ouG5YiG5qC35pysCmBgYAoKYGBge3IgZmlnLndpZHRoPSA0LCBmaWcuaGVpZ2h0PTh9Cm1vdXNlY29yIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBtb3VzZV9jb3JvbmFyeV9jb3VudG1hdHJpeCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qZWN0ID0gIm1vdXNlX2NvciIsIG1pbi5jZWxscyA9IDEwLCBtaW4uZmVhdHVyZXMgPSAzMDApICU+JSAgUGVyY2VudGFnZUZlYXR1cmVTZXQocGF0dGVybiA9ICJebXQtIiwgY29sLm5hbWUgPSAicGVyY2VudC5tdCIpIAoKIyBzYXZlUkRTKG1vdXNlY29yLCJtb3VzZWNvci5yZHMiKQp0YWJsZShtb3VzZWNvciRvcmlnLmlkZW50KQpWbG5QbG90KG1vdXNlY29yLCJuQ291bnRfUk5BIikgLwpWbG5QbG90KG1vdXNlY29yLCJwZXJjZW50Lm10IikgLwpWbG5QbG90KG1vdXNlY29yLCAibkZlYXR1cmVfUk5BIikKCgptb3VzZWNvciA8LSBtb3VzZWNvciAlPiUgc3Vic2V0KHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDQwMCAmIG5GZWF0dXJlX1JOQSA8IDQwMDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuQ291bnRfUk5BID4gMTAwMCAmICBuQ291bnRfUk5BIDwgMzAwMDAgJiBwZXJjZW50Lm10IDwgMTApICU+JQogICAgU0NUcmFuc2Zvcm0odmFycy50by5yZWdyZXNzID0gInBlcmNlbnQubXQiLCB2ZXJib3NlID0gRikgJT4lIAogICAgUnVuUENBKCkgJT4lIEZpbmROZWlnaGJvcnMoZGltcyA9IDE6MjApICU+JSAKICAgIFJ1blVNQVAoZGltcyA9IDE6MjApICU+JSAKICAgIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gMC4xKQp0YWJsZShtb3VzZWNvciRvcmlnLmlkZW50KQpzYXZlUkRTKG1vdXNlY29yLCJtb3VzZWNvci5yZHMiKQpgYGAKCiMjIyBTTUMyCmBgYHtyIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTN9Cm1vdXNlY29yIDwtIHJlYWRSRFMoIm1vdXNlY29yLnJkcyIpCmYoIlByZG0xNiIsbW91c2Vjb3IsIGxhYmVsLnNpemUgPSA3KSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpCmBgYAoKCmBgYHtyIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTN9Cm1vdXNlY29yX3N0cm9tYWwgPC0gc3Vic2V0KG1vdXNlY29yLGlkZW50cyA9IGMoMCwxLDIpKQoKbW91c2Vjb3Jfc3Ryb21hbCA8LSBtb3VzZWNvcl9zdHJvbWFsICU+JSBSdW5QQ0EoKSAlPiUgRmluZE5laWdoYm9ycyhkaW1zID0gMToyMCkgJT4lIAogICAgUnVuVU1BUChkaW1zID0gMToyMCkgJT4lIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gMC4yKQptb3VzZWNvcl9zdHJvbWFsIDwtIG1vdXNlY29yX3N0cm9tYWwgJT4lIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gMC4yKQoKCm1vdXNlY29yX3N0cm9tYWwgPC0gcmVhZFJEUygibW91c2Vjb3Jfc3Ryb21hbC5yZHMiKQoKdW1hcHBsb3QobW91c2Vjb3Jfc3Ryb21hbCwgbGFiZWwuc2l6ZSA9IDYpICsgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkKbXVsdGlfZmVhdHVyZXBsb3QoYygiQWN0YTIiLCJDbm4xIiwiRm4xIiwiTHVtIiwiVmNhbTEiLCJMeTZhIiwiRGx4NSIsIkRseDYiKSwgbW91c2Vjb3Jfc3Ryb21hbCxsYWJlbHMgPSBOQSkKCmxldmVscyhJZGVudHMobW91c2Vjb3Jfc3Ryb21hbCkpIDwtCiAgYygiU01DMSIsIkZpYnJvYmxhc3QxIiwiRmlicm9teW9jeXRlIiwiU01DMSIsIkZpYnJvYmxhc3QyIiwiU01DMiIpCmYoIkFjdGEyIixtb3VzZWNvcl9zdHJvbWFsLGxhYmVsLnNpemUgPSA2KQpwIDwtIG11bHRpX2ZlYXR1cmVwbG90KGMoIkFjdGEyIiwiQ25uMSIsIkZuMSIsIkx1bSIsIlZjYW0xIiwiTHk2YSIsIkRseDUiLCJEbHg2IiwiTGdhbHMzIiksbW91c2Vjb3Jfc3Ryb21hbCxsYWJlbHMgPSBOQSxsYWJlbC5zaXplID0gNikKZ2dzYXZlKCJuYXRtZWRfbW91c2VfY29yb25hcnlfc3Ryb21hbDIucG5nIixwbG90ID0gcCwgaGVpZ2h0ID0gMTIsIHdpZHRoID0gMTYsZGV2aWNlID0gcG5nKQp1bWFwcGxvdChtb3VzZWNvcl9zdHJvbWFsLCBsYWJlbC5zaXplID0gNikgKyB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKQoKbW91c2Vjb3Jfc3Ryb21hbCA8LSBBZGRNb2R1bGVTY29yZShtb3VzZWNvcl9zdHJvbWFsLCBsaXN0KG1tU01DMl9tYXJrZXIpLCBuYW1lID0gIlNNQzJfc2NvcmUiKQpmKCJTTUMyX3Njb3JlMSIsIG1vdXNlY29yX3N0cm9tYWwsbWluLmN1dG9mZiA9IDApCgpgYGAKCiMgbW91c2UgY2Fyb3RpZCBzY1JOQXNlcSAqR1NFMTU1NTEzKgojIyMgZGF0YWxvYWQKYGBge3J9CmNvdW50X21hdHMgPC0gbGlzdC5maWxlcygiLi9HU0UxNTU1MTNfUkFXLyIpCmNvdW50X21hdHMgPC0gY291bnRfbWF0c1tjb3VudF9tYXRzICE9ICJzYW1wbGVpbmZvLnR4dCJdCmFsbExpc3QgPC0gbGFwcGx5KGNvdW50X21hdHMsIGZ1bmN0aW9uKGZpbGUpIHsKICBkZCA8LSByZWFkLnRhYmxlKHBhc3RlMCgiLi9HU0UxNTU1MTNfUkFXLyIsIGZpbGUpLCByb3cubmFtZXMgPSAxLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIGNvbG5hbWVzKGRkKSA8LSBhcy5jaGFyYWN0ZXIoZGRbJ2dlbmUnLF0pCiAgZGQgPC0gZGRbLTEsXQogIENyZWF0ZVNldXJhdE9iamVjdCgKICAgIGNvdW50cyA9IGRkLAogICAgcHJvamVjdCA9IGZpbGUsIG1pbi5jZWxscyA9IDEwLCBtaW4uZmVhdHVyZXMgPSAzMDAKICApCn0pCiMg5ZCI5bm2c2V1cmF05a+56LGhCm1vdXNlX2Nhcm90aWQgPC0gbWVyZ2UoYWxsTGlzdFtbMV1dLCAKICB5ID0gYWxsTGlzdFstMV0sIGFkZC5jZWxsLmlkcyA9IGNvdW50X21hdHMsCiAgcHJvamVjdCA9ICJtb3VzZV9jYXJvdGlkIgopCnJtKGFsbExpc3QpCgojIHNhdmVSRFMobW91c2VfY2Fyb3RpZCwibW91c2VfY2Fyb3RpZC5yZHMiKQpgYGAKCiMjIyBwcm9jZXNzCmBgYHtyfQptb3VzZV9jYXJvdGlkIDwtIHJlYWRSRFMoIm1vdXNlX2Nhcm90aWQucmRzIikKCm1vdXNlX2Nhcm90aWQgPC0gbW91c2VfY2Fyb3RpZCAlPiUgIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KHBhdHRlcm4gPSAiXm10LSIsIGNvbC5uYW1lID0gInBlcmNlbnQubXQiKSAlPiUKc3Vic2V0KHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDUwMCAmIG5GZWF0dXJlX1JOQSA8IDMwMDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuQ291bnRfUk5BID4gMTAwMCAmICBuQ291bnRfUk5BIDwgMjAwMDAgJiBwZXJjZW50Lm10IDwgMTApICU+JQogICAgU0NUcmFuc2Zvcm0odmFycy50by5yZWdyZXNzID0gInBlcmNlbnQubXQiLCB2ZXJib3NlID0gRikgJT4lIAogICAgUnVuUENBKCkgJT4lIEZpbmROZWlnaGJvcnMoZGltcyA9IDE6MjApICU+JSAKICAgIFJ1blVNQVAoZGltcyA9IDE6MjApICU+JSAKICAgIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gMC4xKQoKc2F2ZVJEUyhtb3VzZV9jYXJvdGlkLCJtb3VzZV9jYXJvdGlkLnJkcyIpCmBgYAoKIyMjIFNNQwpgYGB7cn0KbW91c2VfY2Fyb3RpZCA8LSByZWFkUkRTKCJtb3VzZV9jYXJvdGlkLnJkcyIpCnVtYXBwbG90KG1vdXNlX2Nhcm90aWQpCnVtYXBwbG90KG1vdXNlX2Nhcm90aWQsZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIsbGFiZWwgPSBGKQpmKCJQcmRtMTYiLG1vdXNlX2Nhcm90aWQpICNTTUMKZigiTHk2YSIsbW91c2VfY2Fyb3RpZCkgI1NFTS1saWtlIGNlbGxzCgptdWx0aV9mZWF0dXJlcGxvdChjKCJCbXAyIiwiQm1wNCIsIkJtcDYiKSxtb3VzZV9jYXJvdGlkKQoKIyMgQk1QNCDlnKjov5nph4xFQ+eahCrlpKfpg6jliIYq5Lit6KGo6L6+77yM6ICM5Zyo5Lq657G75qC35pys5LitQk1QNCsgRUPnu4bog57mmK/lsJHmlbDnmoQKbWFya2VycyA8LSBGaW5kQWxsTWFya2Vycyhtb3VzZV9jYXJvdGlkLGxvZ2ZjLnRocmVzaG9sZCA9IDAuNSxtaW4uZGlmZi5wY3QgPSAwLjIsIG9ubHkucG9zID0gVCkKYGBgCgojIyMgc3Ryb21hbCBjZWxscwpgYGB7ciBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0zfQptb3VzZV9jYXJvdGlkX3N0cm9tYWwgPC0gc3Vic2V0KG1vdXNlX2Nhcm90aWQsaWRlbnRzID0gYygwLDIsMSw3KSkKbW91c2VfY2Fyb3RpZF9zdHJvbWFsIDwtIG1vdXNlX2Nhcm90aWRfc3Ryb21hbCAlPiUgUnVuUENBKCkgJT4lIEZpbmROZWlnaGJvcnMoZGltcyA9IDE6MjApICU+JSAKICAgIFJ1blVNQVAoZGltcyA9IDE6MjApICU+JSBGaW5kQ2x1c3RlcnMocmVzb2x1dGlvbiA9IDAuMSkKbW91c2VfY2Fyb3RpZF9zdHJvbWFsIDwtIG1vdXNlX2Nhcm90aWRfc3Ryb21hbCAlPiUgRmluZENsdXN0ZXJzKHJlc29sdXRpb24gPSAwLjIpCgptb3VzZV9jYXJvdGlkX3N0cm9tYWwgPC0gcmVhZFJEUygibW91c2VfY2Fyb3RpZF9zdHJvbWFsLnJkcyIpCiMgc2F2ZVJEUyhtb3VzZV9jYXJvdGlkX3N0cm9tYWwsIm1vdXNlX2Nhcm90aWRfc3Ryb21hbC5yZHMiKQoKdW1hcHBsb3QobW91c2VfY2Fyb3RpZF9zdHJvbWFsKQpmKCJEbHgyIixtb3VzZV9jYXJvdGlkX3N0cm9tYWwpICNEbHgyLERseDUsRGx4NuWFseWQjOWumuS9jQoKbW91c2VfY2Fyb3RpZF9zdHJvbWFsIDwtIEFkZE1vZHVsZVNjb3JlKG1vdXNlX2Nhcm90aWRfc3Ryb21hbCwgbGlzdChtbVNNQzJfbWFya2VyKSwgbmFtZSA9ICJTTUMyX3Njb3JlIikKZigiU01DMl9zY29yZTEiLCBtb3VzZV9jYXJvdGlkX3N0cm9tYWwsbWluLmN1dG9mZiA9IDApCgptdWx0aV9mZWF0dXJlcGxvdChtbVNNQzJfbWFya2VyWzE6OV0sbW91c2VfY2Fyb3RpZF9zdHJvbWFsLGxhYmVscyA9IE5BLCBsYWJlbCA9IEYpCmBgYAoKIyMjIyDliIbnvqTooagKYGBge3IgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9M30KRGx4NXBvc2NlbGxzIDwtIFdoaWNoQ2VsbHMobW91c2VfY2Fyb3RpZF9zdHJvbWFsLCBleHByZXNzaW9uID0gYERseDZgID4gMCAmIGBEbHg1YCA+IDApCmdyb3VwX3RhYiA8LSBJZGVudHMobW91c2VfY2Fyb3RpZF9zdHJvbWFsKQp0YWJsZShncm91cF90YWJbRGx4NXBvc2NlbGxzXSkvdGFibGUoZ3JvdXBfdGFiKSAKIyDlnKhjbHVzdGVyNSDmnIk0My44JeeahOe7huiDnuihqOi+vkRMWDXvvIwzOC40JeeahOe7huiDnuihqOi+vkRMWDYsMjAuMiXnmoTnu4bog57ooajovr7kuKTogIUKCiMjIOWFs+S6juagt+acrOS/oeaBrwpEbHg1cG9zY2VsbHMgPC0gV2hpY2hDZWxscyhtb3VzZV9jYXJvdGlkX3N0cm9tYWwsIGlkZW50cyA9ICJETFggU01DIikKZ3JvdXBfdGFiIDwtIG1vdXNlX2Nhcm90aWRfc3Ryb21hbCRvcmlnLmlkZW50CnRhYmxlKGdyb3VwX3RhYltEbHg1cG9zY2VsbHNdKS90YWJsZShncm91cF90YWIpIAp0YWJsZShncm91cF90YWJbRGx4NXBvc2NlbGxzXSkKYGBgCgojIyMjIOWbvgpgYGB7ciBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0zfQpsZXZlbHMoSWRlbnRzKG1vdXNlX2Nhcm90aWRfc3Ryb21hbCkpIDwtIGMoIlNFTSBjZWxsIiwiRmlicm9ibGFzdDEiLCJTTUMiLCJTTUMiLCJGaWJyb2JsYXN0MiIsIkRMWCBTTUMiLCJVbmFubm90YXRlZCIsIlVuYW5ub3RhdGVkIikKdW1hcHBsb3QobW91c2VfY2Fyb3RpZF9zdHJvbWFsLGxhYmVsLnNpemUgPSA2LGxhYmVsID0gRikgJT4lIGdnc2F2ZSgiLi9maWc3X21vdXNlL2Npcl9tb3VzZV9jYXJvdGlkX3N0cm9tYWx1bWFwMi5wbmciLHBsb3QgPSAuLCBoZWlnaHQgPSA1LCB3aWR0aCA9IDYsZGV2aWNlID0gcG5nKQoKcCA8LSBtdWx0aV9mZWF0dXJlcGxvdChjKCJBY3RhMiIsIkNubjEiLCJGbjEiLCJMdW0iLCJWY2FtMSIsIkx5NmEiLCJEbHg1IiwiRGx4NiIsIkxnYWxzMyIpLG1vdXNlX2Nhcm90aWRfc3Ryb21hbCxsYWJlbHMgPSBOQSwgbGFiZWwuc2l6ZSA9IDYsIGxhYmVsID0gRikKZ2dzYXZlKCIuL2ZpZzdfbW91c2UvY2lyX21vdXNlX2Nhcm90aWRfc3Ryb21hbDIucG5nIiAscGxvdCA9IHAsIGhlaWdodCA9IDEyLCB3aWR0aCA9IDE2LGRldmljZSA9IHBuZykKRG90cGxvdChjKCJBY3RhMiIsIkNubjEiLCJGbjEiLCJMdW0iLCJWY2FtMSIsIkx5NmEiLCJEbHg1IiwiRGx4NiIsIlNvc3QiKSxtb3VzZV9jYXJvdGlkX3N0cm9tYWwpCgpETFhwb3NTTUNfbWFya2VycyA8LSBGaW5kTWFya2Vycyhtb3VzZV9jYXJvdGlkX3N0cm9tYWwsIGlkZW50LjEgPSAiRExYIFNNQyIsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuNCwgb25seS5wb3MgPSBULCBtaW4uZGlmZi5wY3QgPSAwLjIpCmYoIkRseDUiLG1vdXNlX2Nhcm90aWRfc3Ryb21hbCkKCmxpYnJhcnkob3JnLk1tLmVnLmRiKQpHT19kb3RwbG90KHJvd25hbWVzKERMWHBvc1NNQ19tYXJrZXJzKSwgT3JnRGIgPSBvcmcuTW0uZWcuZGIpCmBgYAoKCiMjIyMgWEdCb29zdCDot6jniannp40KYGBge3J9CmxpYnJhcnkoaG9tb2xvZ2VuZSkKbW91c2VfZ2VuZTJodW1hbl9nZW5lIDwtIGZ1bmN0aW9uKGdlbmVsaXN0KXsKICBtbWdlbmVzIDwtIGhvbW9sb2dlbmUoZ2VuZWxpc3QsIGluVGF4ID0gMTAwOTAsIG91dFRheCA9IDk2MDYpCiAgbW1nZW5lcyA8LSBtbWdlbmVzWyFkdXBsaWNhdGVkKG1tZ2VuZXMkYDEwMDkwYCksXQogIGdlbmVsaXN0W2dlbmVsaXN0ICVpbiUgbW1nZW5lcyRgMTAwOTBgXSA8LSBtbWdlbmVzJGA5NjA2YAogIHJldHVybihnZW5lbGlzdCkKfQptb3VzZV9jYXJvdGlkX3N0cm9tYWxAYXNzYXlzW1siU0NUIl1dQGRhdGFARGltbmFtZXNbWzFdXSA8LQogIG1vdXNlX2Nhcm90aWRfc3Ryb21hbEBhc3NheXNbWyJTQ1QiXV1AZGF0YUBEaW1uYW1lc1tbMV1dICU+JSBtb3VzZV9nZW5lMmh1bWFuX2dlbmUoKQoKbW91c2VfY2Fyb3RpZF9zdHJvbWFsQGFzc2F5c1tbIlNDVCJdXUB2YXIuZmVhdHVyZXMgPC0gbW91c2VfY2Fyb3RpZF9zdHJvbWFsQGFzc2F5c1tbIlNDVCJdXUB2YXIuZmVhdHVyZXMgJT4lIG1vdXNlX2dlbmUyaHVtYW5fZ2VuZSgpCgpmKCJETFg2IiwgbW91c2VfY2Fyb3RpZF9zdHJvbWFsKQoKbW91c2VfY2Fyb3RpZF9zdHJvbWFsIDwtIHF1ZXJ5X3NjbWFwX2Zyb21fcmVmc2NlKG1vdXNlX2Nhcm90aWRfc3Ryb21hbCwgcmVmX3NjZSkKCiMgcnVuIHhnYm9vc3Qgd3JhcHBlcgptb3VzZV9jYXJvdGlkX3N0cm9tYWwgPC0gWEdCb29zdF9wcmVkaWN0X2Zyb21fc2V1b2JqKG1vdXNlX2Nhcm90aWRfc3Ryb21hbCwgYnN0X21vZGVsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsdHlwZV9hc3NpZ24gPSAyKQptb3VzZV9jYXJvdGlkX3N0cm9tYWwgPC0gcHJvamVjdDJyZWZfY2VsbHR5cGUobW91c2VfY2Fyb3RpZF9zdHJvbWFsLCBkczIpCnVtYXBwbG90KG1vdXNlX2Nhcm90aWRfc3Ryb21hbCwgZ3JvdXAuYnkgPSAicmVmX2NlbGx0eXBlIikKdW1hcHBsb3QobW91c2VfY2Fyb3RpZF9zdHJvbWFsLCBncm91cC5ieSA9ICJzY21hcF9pZGVudHMiKQp0YWJsZShtb3VzZV9jYXJvdGlkX3N0cm9tYWwkcmVmX2NlbGx0eXBlKQpgYGAKCgpgYGB7cn0KbW91c2Vjb3Jfc3Ryb21hbEBhc3NheXNbWyJTQ1QiXV1AZGF0YUBEaW1uYW1lc1tbMV1dIDwtCiAgbW91c2Vjb3Jfc3Ryb21hbEBhc3NheXNbWyJTQ1QiXV1AZGF0YUBEaW1uYW1lc1tbMV1dICU+JSBtb3VzZV9nZW5lMmh1bWFuX2dlbmUoKQoKbW91c2Vjb3Jfc3Ryb21hbEBhc3NheXNbWyJTQ1QiXV1AdmFyLmZlYXR1cmVzIDwtIG1vdXNlY29yX3N0cm9tYWxAYXNzYXlzW1siU0NUIl1dQHZhci5mZWF0dXJlcyAlPiUgbW91c2VfZ2VuZTJodW1hbl9nZW5lKCkKCmYoIkRMWDYiLCBtb3VzZWNvcl9zdHJvbWFsKQoKbW91c2Vjb3Jfc3Ryb21hbCA8LSBxdWVyeV9zY21hcF9mcm9tX3JlZnNjZShtb3VzZWNvcl9zdHJvbWFsLCByZWZfc2NlKQoKIyBydW4geGdib29zdCB3cmFwcGVyCm1vdXNlY29yX3N0cm9tYWwgPC0gWEdCb29zdF9wcmVkaWN0X2Zyb21fc2V1b2JqKG1vdXNlY29yX3N0cm9tYWwsIGJzdF9tb2RlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VsbHR5cGVfYXNzaWduID0gMikKbW91c2Vjb3Jfc3Ryb21hbCA8LSBwcm9qZWN0MnJlZl9jZWxsdHlwZShtb3VzZWNvcl9zdHJvbWFsLCBkczIpCnVtYXBwbG90KG1vdXNlY29yX3N0cm9tYWwsIGdyb3VwLmJ5ID0gInJlZl9jZWxsdHlwZSIpCnVtYXBwbG90KG1vdXNlY29yX3N0cm9tYWwsIGdyb3VwLmJ5ID0gInNjbWFwX2lkZW50cyIpCnRhYmxlKG1vdXNlY29yX3N0cm9tYWwkcmVmX2NlbGx0eXBlKQpgYGAKCiMgaHVtYW4gZHMyCmBgYHtyfQpwIDwtIG11bHRpX2ZlYXR1cmVwbG90KGMoIkFDVEEyIiwiQ05OMSIsIkZOMSIsIkxVTSIsIlZDQU0xIiwiTFk2QSIsIkRMWDUiLCJETFg2IiwiTEdBTFMzIiwiU09TVCIpLCBkczIsIGxhYmVscyA9IE5BLCBsYWJlbC5zaXplID0gNikKCmdnc2F2ZSgicmVmZHMyX2Nhcm90aWRfc3Ryb21hbC5wbmciLHBsb3QgPSBwLCBoZWlnaHQgPSAxMiwgd2lkdGggPSAxMixkZXZpY2UgPSBwbmcpCgpwIDwtIG11bHRpX2ZlYXR1cmVwbG90KGMoIkZSWkIiLCJTT1NUIiwiRExYNSIsIkRMWDYiKSwgZHMyLCBsYWJlbHMgPSBOQSwgbGFiZWwuc2l6ZSA9IDYpCmdnc2F2ZSgicmVmZHMyX1NNQzJfY2Fyb3RpZF9zdHJvbWFsLnBuZyIscGxvdCA9IHAsIGhlaWdodCA9IDcsIHdpZHRoID0gNyxkZXZpY2UgPSBwbmcpCmBgYAoKCiMgaHVtYW4gYnVsayBSTkEtc2VxICpHU0UxMjA1MjEqIGNhcm90aWQgc3RhYmxlL3Vuc3RhYmxlIEZQS00KYGBge3J9CmZwa20ydHBtIDwtIGZ1bmN0aW9uKGZwa20pewogIGV4cChsb2coZnBrbSkgLSBsb2coc3VtKGZwa20pKSArIGxvZygxZTYpKQp9CgpmcGttX21hdHJpeCA8LSByZWFkLmNzdigiR1NFMTIwNTIxX0ZQS00uY3N2IikKIyBmcGttX21hdHJpeCA8LSBkaXN0aW5jdChmcGttX21hdHJpeCkgI+WOu+mZpOmHjeWkjeihjApmcGttX21hdHJpeCA8LSBmcGttX21hdHJpeFshZHVwbGljYXRlZChmcGttX21hdHJpeCRuYW1lKSxdCnJvd25hbWVzKGZwa21fbWF0cml4KSA8LSBmcGttX21hdHJpeCRuYW1lCmZwa21fbWF0cml4JG5hbWUgPC0gTlVMTAoKdHBtX21hdHJpeCA8LSBhcHBseShmcGttX21hdHJpeCwgMiwgZnBrbTJ0cG0pCmNvbFN1bXModHBtX21hdHJpeCkKCmdyb3VwX2ZpbGUgPC0gYygic3RhYmxlIiwidW5zdGFibGUiLCJzdGFibGUiLCJ1bnN0YWJsZSIsCiAgICAgICAgICAgICAgICAic3RhYmxlIiwidW5zdGFibGUiLCJzdGFibGUiLCJ1bnN0YWJsZSIpCmJveHBsb3QodHBtX21hdHJpeCwgbGFzID0gMikKCmV4cHJfbWF0IDwtIHRwbV9tYXRyaXhbIWFwcGx5KHRwbV9tYXRyaXgsIDEsIGZ1bmN0aW9uKHgpe3N1bShmbG9vcih4KSA9PSAwKT4zfSksXQoKYm94cGxvdChleHByX21hdCwgbGFzID0gMikKCmxpYnJhcnkobGltbWEpCmV4cHJfbWF0IDwtIG5vcm1hbGl6ZUJldHdlZW5BcnJheXMoZXhwcl9tYXQpCmV4cHJfbWF0IDwtIGxvZzIoZXhwcl9tYXQrMSkgI+S9v+eUqGxvZzIgc2NhbGUKCiNQQ0EKbGlicmFyeShnZ2ZvcnRpZnkpIApkZiA8LSBhcy5kYXRhLmZyYW1lKHQoZXhwcl9tYXQpKSAKZGYkZ3JvdXAgPC0gZ3JvdXBfZmlsZSAKYXV0b3Bsb3QocHJjb21wKGRmWywxOihuY29sKGRmKS0xKV0pLCBkYXRhPWRmLCBjb2xvdXIgPSAnZ3JvdXAnKSsgdGhlbWVfYncoKSAKCiMgSWYgdGhlIHNlcXVlbmNpbmcgZGVwdGggaXMgcmVhc29uYWJseSBjb25zaXN0ZW50IGFjcm9zcyB0aGUgUk5BIHNhbXBsZXMsIHRoZW4gdGhlIHNpbXBsZXN0IGFuZCBtb3N0IHJvYnVzdCBhcHByb2FjaCB0byBkaWZmZXJlbnRpYWwgZXhpcyB0byB1c2UgbGltbWEtdHJlbmQuCgpmaXQgPC0gbG1GaXQoZXhwcl9tYXQsIGdyb3VwX2ZpbGUpCmZpdCA8LSB0cmVhdChmaXQsIGxmYz1sb2cyKDEuMiksIHRyZW5kPVRSVUUpCnRvcFRyZWF0KGZpdCwgY29lZj1uY29sKGRlc2lnbikpCgpgYGAKCgpgYGB7cn0KbGlicmFyeShnZ3B1YnIpCmRhdCA8LSBleHByX21hdApkZXNpZ24gPC0gbW9kZWwubWF0cml4KH5mYWN0b3IoZ3JvdXBfZmlsZSkpCmZpdCA8LSBsbUZpdChkYXQsIGRlc2lnbikKZml0IDwtIGVCYXllcyhmaXQpCiMgb3B0aW9ucyhkaWdpdHMgPSA0KQp0b3BUYWJsZShmaXQsY29lZj0yLGFkanVzdD0nQkgnKQpkZWcgPC0gdG9wVGFibGUoZml0LGNvZWY9MixhZGp1c3Q9J0JIJyxudW1iZXIgPSBJbmYpCmhlYWQoZGVnKSAKCndyaXRlLmNzdihkZWcsIi4vZGF0YXRhYmxlL3N0YWJsZSB2cyB1bnN0YWJsZS5jc3YiKQojIyBsb29rIHVwIEZSWkIsIFNPU1QsIFBSRE02LCBPR04KZHMxbWFya2Vyc1tkczFtYXJrZXJzJGNsdXN0ZXIgPT0gIlNNQzIiLF0kZ2VuZQoKZHMybWFya2Vyc1tkczJtYXJrZXJzJGNsdXN0ZXIgPT0gIlNNQzMiLF0kZ2VuZQoKc21jMm1hcmtlcnMgPC0gaW50ZXJzZWN0KGRzMW1hcmtlcnNbZHMxbWFya2VycyRjbHVzdGVyID09ICJTTUMyIixdJGdlbmUsIGRzMm1hcmtlcnNbZHMybWFya2VycyRjbHVzdGVyID09ICJTTUMzIixdJGdlbmUpCgoKZGVnW2ludGVyc2VjdChzbWMybWFya2Vycywgcm93bmFtZXMoZGVnKSksXQoKZHMxbWFya2Vyc1tkczFtYXJrZXJzJGNsdXN0ZXIgPT0gIlNNQzEiLF0kZ2VuZQoKZHMybWFya2Vyc1tkczJtYXJrZXJzJGNsdXN0ZXIgPT0gIlNNQzEiLF0kZ2VuZQoKU01DMW1hcmtlcnMgPC0gaW50ZXJzZWN0KGRzMW1hcmtlcnNbZHMxbWFya2VycyRjbHVzdGVyID09ICJTTUMxIixdJGdlbmUsIGRzMm1hcmtlcnNbZHMybWFya2VycyRjbHVzdGVyID09ICJTTUMxIixdJGdlbmUpCgpkZWdbaW50ZXJzZWN0KFNNQzFtYXJrZXJzLCByb3duYW1lcyhkZWcpKSxdCmBgYAoKYGBge3J9CiMgbG9nRkNfdGhyZXNob2xkID0gMQojIHB2YWx1ZV90aHJlc2hvbGQgPSAwLjA1CnNlbGVjdGVkX2dlbmVzID0gYXMuY2hhcmFjdGVyKHJlYWQudGFibGUoIlNNQzIiKSRWMSkKCnZvbGNhbm9fcGxvdCA8LSBmdW5jdGlvbihmaWxlbmFtZSwgc2VsZWN0ZWRfZ2VuZXMsIGxvZ0ZDX3RocmVzaG9sZCA9IDEsIHB2YWx1ZV90aHJlc2hvbGQgPSAwLjA1KQp7CiAgZjwtcmVhZC5jc3YoIi4vZGF0YXRhYmxlL3N0YWJsZSB2cyB1bnN0YWJsZS5jc3YiKQogIGYkdGhyZXNob2xkIDwtIGZhY3RvcihpZmVsc2UoZiRhZGouUC5WYWwgPCBwdmFsdWVfdGhyZXNob2xkICYgYWJzKGYkbG9nRkMpID49IGxvZ0ZDX3RocmVzaG9sZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShmJGxvZ0ZDPj0gbG9nRkNfdGhyZXNob2xkICwnVXAnLCdEb3duJyksJ04uUy4nKSwKICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygnVXAnLCdEb3duJywnTi5TLicpKQogIAogICBnZ3Bsb3QoZixhZXMoeD1sb2dGQyx5PS1sb2cxMChhZGouUC5WYWwpLGNvbG9yPXRocmVzaG9sZCkpKwogICAgZ2VvbV9wb2ludCgpKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjQ0MwMDAwIiwiIzJmNTY4OCIsIiNCQkJCQkIiKSkrCiAgICBnZW9tX3RleHRfcmVwZWwoCiAgICAgIGRhdGEgPSBmW2YkWCAlaW4lIHNlbGVjdGVkX2dlbmVzLF0sCiAgICAgIGFlcyhsYWJlbCA9IFgpLAogICAgICBzaXplID0gNSwgbWF4Lm92ZXJsYXBzID0gMTAwMCwKICAgICAgY29sPSJibGFjayIsIHNlZ21lbnQuY29sb3IgPSAiYmxhY2siLCBzaG93LmxlZ2VuZCA9IEZBTFNFICkrCiAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpKSsKICAgIHRoZW1lKAogICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksIAogICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLCAKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpCiAgICApKwogICAgeWxhYignLWxvZzEwIChwLWFkaiknKSArCiAgICB4bGFiKCdsb2cyIChGb2xkQ2hhbmdlKScpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKC1sb2dGQ190aHJlc2hvbGQsbG9nRkNfdGhyZXNob2xkKSwgbHR5PTMsY29sPSJibGFjayIsbHdkPTAuNSkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID1jKDAsLWxvZzEwKHB2YWx1ZV90aHJlc2hvbGQpKSxsdHk9Myxjb2w9ImJsYWNrIixsd2Q9MC41KQp9CgpzZWxlY3RlZF9nZW5lcyA8LSBjKGFzLmNoYXJhY3RlcihyZWFkLnRhYmxlKCJTTUMyIikkVjEpKQoKcCA8LSB2b2xjYW5vX3Bsb3QoIi4vZGF0YXRhYmxlL3N0YWJsZSB2cyB1bnN0YWJsZS5jc3YiLHNlbGVjdGVkX2dlbmVzKSArIGdndGl0bGUoInN0YWJsZSB2cyB1bnN0YWJsZSwgU01DMiBtYXJrZXIiKQoKZ2dzYXZlKCJTTUMyX3N0YWJsZSB2cyB1bnN0YWJsZS5wbmciLHBsb3QgPSBwLGRldmljZSA9IHBuZyxoZWlnaHQgPSA0LCB3aWR0aCA9IDYpCmBgYAoKCkFkZCBhIG5ldyBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKi4KCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ3RybCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLgoKVGhlIHByZXZpZXcgc2hvd3MgeW91IGEgcmVuZGVyZWQgSFRNTCBjb3B5IG9mIHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLiBDb25zZXF1ZW50bHksIHVubGlrZSAqS25pdCosICpQcmV2aWV3KiBkb2VzIG5vdCBydW4gYW55IFIgY29kZSBjaHVua3MuIEluc3RlYWQsIHRoZSBvdXRwdXQgb2YgdGhlIGNodW5rIHdoZW4gaXQgd2FzIGxhc3QgcnVuIGluIHRoZSBlZGl0b3IgaXMgZGlzcGxheWVkLgo=